package hmvictor.jpql;

import hmvictor.jpql.renderers.ConnectionListRenderer;
import java.awt.Frame;
import java.awt.event.ActionEvent;
import java.awt.event.InputEvent;
import java.awt.event.KeyEvent;
import java.awt.event.WindowAdapter;
import java.awt.event.WindowEvent;
import java.beans.IntrospectionException;
import java.beans.Introspector;
import java.beans.PropertyDescriptor;
import java.io.File;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.io.PrintWriter;
import java.lang.reflect.InvocationTargetException;
import java.net.MalformedURLException;
import java.net.URL;
import java.net.URLClassLoader;
import java.nio.file.Files;
import java.util.Collections;
import java.util.HashMap;
import java.util.HashSet;
import java.util.LinkedList;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.concurrent.ExecutionException;
import javax.persistence.EntityManager;
import javax.persistence.EntityManagerFactory;
import javax.persistence.Parameter;
import javax.persistence.Persistence;
import javax.persistence.Query;
import javax.swing.AbstractAction;
import javax.swing.Action;
import javax.swing.DefaultComboBoxModel;
import javax.swing.JFrame;
import javax.swing.KeyStroke;
import javax.swing.SwingWorker;
import javax.swing.event.DocumentEvent;
import javax.swing.event.DocumentListener;
import javax.swing.table.DefaultTableModel;
import javax.swing.text.BadLocationException;
import javax.xml.parsers.ParserConfigurationException;
import javax.xml.xpath.XPathExpressionException;
import org.netbeans.api.settings.ConvertAsProperties;
import org.openide.DialogDisplayer;
import org.openide.NotifyDescriptor;
import org.openide.NotifyDescriptor.Confirmation;
import org.openide.awt.ActionID;
import org.openide.awt.ActionReference;
import org.openide.cookies.SaveCookie;
import org.openide.filesystems.FileChooserBuilder;
import org.openide.nodes.AbstractNode;
import org.openide.nodes.Children;
import org.openide.nodes.Node;
import org.openide.util.Exceptions;
import org.openide.util.NbBundle.Messages;
import org.openide.util.lookup.AbstractLookup;
import org.openide.util.lookup.InstanceContent;
import org.openide.util.lookup.Lookups;
import org.openide.util.lookup.ProxyLookup;
import org.openide.windows.TopComponent;
import org.openide.windows.WindowManager;
import org.openide.windows.WindowSystemEvent;
import org.openide.windows.WindowSystemListener;
import org.xml.sax.SAXException;

/**
 * Top component which displays something.
 */
@ConvertAsProperties(
        dtd = "-//hmvictor.jpql//JQPLEditor//EN",
        autostore = false
)
@TopComponent.Description(
        preferredID = "JQPLEditorTopComponent",
        //iconBase="SET/PATH/TO/ICON/HERE", 
        persistenceType = TopComponent.PERSISTENCE_ALWAYS
)
@TopComponent.Registration(mode = "editor", openAtStartup = true)
@ActionID(category = "Window", id = "hmvictor.jpql.JQPLEditorTopComponent")
@ActionReference(path = "Menu/Window" /*, position = 333 */)
@TopComponent.OpenActionRegistration(
        displayName = "#CTL_JQPLEditorAction",
        preferredID = "JQPLEditorTopComponent"
)
@Messages({
    "CTL_JQPLEditorAction=JQPLEditor",
    "CTL_JQPLEditorTopComponent=JQPLEditor Window",
    "HINT_JQPLEditorTopComponent=This is a JQPLEditor window"
})
public final class JPQLEditorTopComponent extends TopComponent {

    private static final char SEPARATOR = ';';

    private File currentFile;
    private InstanceContent ic = new InstanceContent();
    private List<File> recentFiles = new LinkedList<>();
    private final ParameterCache parameterCache = new ParameterCache();
    private List<Connection> connections = new LinkedList<>();

    private final Action executeQueryAction = new AbstractAction("Execute...") {

        @Override
        public void actionPerformed(ActionEvent e) {
            String query = queryEditor.getSelectedText();
            if (query == null) {
                query = Queries.getQuery(queryEditor.getText(), queryEditor.getCaretPosition());
            }
            Connection connection = (Connection) connectionCombo.getSelectedItem();
            if (connection.getPassword() == null) {
                char[] password = ConnectionDialog.showPasswordDialog(JPQLEditorTopComponent.this, connection);
                if (password != null) {
                    connection.setPassword(password);
                }
            }
            if (connection.getPassword() != null) {
                try {
                    new QueryBuilder(connection, query, unitnamesCombo.getSelectedItem().toString()).execute();
                } catch (MalformedURLException ex) {
                    Exceptions.printStackTrace(ex);
                }
            }
        }

    };

    public File getFile() {
        return currentFile;
    }

    public void saveContent(File file) {
        currentFile = file;
        new SaveToFileWorker(currentFile, queryEditor.getText()).execute();
    }

    public boolean isThereUnsavedContent() throws IOException {
        String content = queryEditor.getText();
        if (currentFile == null) {
            return !content.isEmpty();
        } else {
            String fileContent = new String(Files.readAllBytes(currentFile.toPath()));
            return !fileContent.equals(content);
        }
    }

    public void setNewContent() {
        currentFile = null;
        queryEditor.setText("");
        setDisplayName("Unsaved");
        setToolTipText("Content not saved");
    }

    public String getSelectedText() {
        return queryEditor.getSelectedText();
    }

    private class QueryBuilder extends SwingWorker<Query, Void> {

        private final Connection connection;
        private final String queryText;
        private final String unitName;
        private EntityManager em;
        private final List<URL> urls = new LinkedList<>();

        public QueryBuilder(Connection connection, String queryText, String unitName) throws MalformedURLException {
            this.connection = connection;
            this.queryText = queryText;
            this.unitName = unitName;
            JarExplorerTopComponent jarExplorer = (JarExplorerTopComponent) WindowManager.getDefault().findTopComponent("JarExplorerTopComponent");
            urls.addAll(jarExplorer.getJarUrls());
            DriversTopComponent driversTopComponent = (DriversTopComponent) WindowManager.getDefault().findTopComponent("DriversTopComponent");
            urls.addAll(driversTopComponent.getJarUrls());
            executeQueryAction.setEnabled(false);
            busyIndicator.setBusy(true);
        }

        @Override
        protected Query doInBackground() throws Exception {
            ClassLoader loader = new URLClassLoader(urls.toArray(new URL[urls.size()]), Thread.currentThread().getContextClassLoader());
            Thread.currentThread().setContextClassLoader(loader);
            Map<String, String> map = new HashMap();
            map.put("javax.persistence.jdbc.url", connection.getUrl());
            map.put("javax.persistence.jdbc.password", connection.getUser());
            map.put("javax.persistence.jdbc.user", new String(connection.getPassword()));
            map.put("javax.persistence.schema-generation.database.action", "none");
            EntityManagerFactory ef = Persistence.createEntityManagerFactory(unitName, map);
            em = ef.createEntityManager(map);
            final Query query = em.createQuery(queryText);
            return query;
        }

        @Override
        protected void done() {
            try {
                Query query = get();
                busyIndicator.setBusy(false);
                final Set<Parameter<?>> parameters = query.getParameters();
                if (!parameters.isEmpty()) {
                    boolean value = ParameterDialog.showParametersDialog(JPQLEditorTopComponent.this, query, parameters, parameterCache);
                    if (value) {
                        new QueryResult(em, query).execute();
                    }
                } else {
                    new QueryResult(em, query).execute();
                }
            } catch (InterruptedException | ExecutionException ex) {
                Exceptions.printStackTrace(ex);
            } finally {
                executeQueryAction.setEnabled(true);
                busyIndicator.setBusy(false);
            }
        }

    }

    private class QueryResult extends SwingWorker<List<Object>, Object> {

        private final EntityManager em;
        private final Query query;

        public QueryResult(EntityManager em, Query query) {
            this.em = em;
            this.query = query;
            executeQueryAction.setEnabled(false);
            busyIndicator.setBusy(true);
            tableResult.setModel(new DefaultTableModel());
        }

        @Override
        protected List<Object> doInBackground() throws Exception {
            return query.getResultList();
        }

        @Override
        protected void done() {
            try {
                List<Object> result = get();
                setResult(em, result);
            } catch (InterruptedException | ExecutionException ex) {
                Exceptions.printStackTrace(ex);
            } finally {
                em.close();
                executeQueryAction.setEnabled(true);
                busyIndicator.setBusy(false);
            }
        }

    }

//    private class SaveCookieImpl implements SaveCookie {
//
//        public void save() throws IOException {
//            Confirmation msg = new NotifyDescriptor.Confirmation("Do you want to save", NotifyDescriptor.OK_CANCEL_OPTION,
//                    NotifyDescriptor.QUESTION_MESSAGE
//            );
//            Object result = DialogDisplayer.getDefault().notify(msg);
//                //When user clicks "Yes", indicating they really want to save,
//            //we need to disable the Save button and Save menu item,
//            //so that it will only be usable when the next change is made
//            //to the text field:
//            if (NotifyDescriptor.YES_OPTION.equals(result)) {
////                    fire(false);
//                //Implement your save functionality here.
//            }
//        }
//    }
//    
//    private SaveCookie impl=new SaveCookieImpl();
//    private class QueryNode extends AbstractNode {
//
//        private SaveCookieImpl impl;
//
//        public QueryNode() {
//            super(Children.LEAF);
//            impl = new SaveCookieImpl();
//        }
//
//        public void fire(boolean modified) {
//            if (modified) {
//                //If the text is modified,
//                //we implement SaveCookie,
//                //and add the implementation to the cookieset:
////                getCookieSet().assign(SaveCookie.class, impl);
//                ic.add(impl);
//            } else {
//                //Otherwise, we make no assignment
//                //and the SaveCookie is not made available:
////                getCookieSet().assign(SaveCookie.class);
//                ic.remove(impl);
//            }
//        }
//
//        private class SaveCookieImpl implements SaveCookie {
//
//            public void save() throws IOException {
//                Confirmation msg = new NotifyDescriptor.Confirmation("Do you want to save", NotifyDescriptor.OK_CANCEL_OPTION,
//                        NotifyDescriptor.QUESTION_MESSAGE
//                );
//                Object result = DialogDisplayer.getDefault().notify(msg);
//                //When user clicks "Yes", indicating they really want to save,
//                //we need to disable the Save button and Save menu item,
//                //so that it will only be usable when the next change is made
//                //to the text field:
//                if (NotifyDescriptor.YES_OPTION.equals(result)) {
//                    fire(false);
//                    //Implement your save functionality here.
//                }
//            }
//        }
//
//    }
//    private class QuerySavable extends AbstractSavable implements SaveAsCapable {
//
//        private final String content;
//
//        public QuerySavable(String content) {
//            this.content = content;
//            register();
//        }
//
//        @Override
//        public void saveAs(FileObject directory, String filename) throws IOException {
//            FileObject file = FileUtil.createData(directory, filename);
//            new SaveToFileWorker(FileUtil.toFile(file), content, this).execute();
//        }
//
//        @Override
//        protected String findDisplayName() {
//            return "Content";
//        }
//
//        @Override
//        protected void handleSave() throws IOException {
//            if (currentFile != null) {
//                new SaveToFileWorker(currentFile, content, this).execute();
//            } else {
//                NotifyDescriptor.Message message = new NotifyDescriptor.Message(NbBundle.getMessage(JPQLEditorTopComponent.class, "currentFile.none"), NotifyDescriptor.WARNING_MESSAGE);
//                DialogDisplayer.getDefault().notify(message);
//                throw new IOException();
//            }
//        }
//
//        public void publicUnregister() {
//            unregister();
//            ic.remove(this);
//        }
//
//        @Override
//        public int hashCode() {
//            int hash = 7;
//            hash = 59 * hash + Objects.hashCode(this.content);
//            return hash;
//        }
//
//        @Override
//        public boolean equals(Object obj) {
//            if (obj == null) {
//                return false;
//            }
//            if (getClass() != obj.getClass()) {
//                return false;
//            }
//            final QuerySavable other = (QuerySavable) obj;
//            if (!Objects.equals(this.content, other.content)) {
//                return false;
//            }
//            return true;
//        }
//
//    }
    private class SaveToFileWorker extends SwingWorker<File, Void> {

        private final File file;
        private final String content;

        public SaveToFileWorker(File file, String content) {
            this.file = file;
            this.content = content;
        }

        @Override
        protected File doInBackground() throws Exception {
            Files.write(file.toPath(), queryEditor.getText().getBytes());
            return file;
        }

        @Override
        protected void done() {
            try {
                File f = get();
                currentFile = f;
                setDisplayName(currentFile.getName());
                setToolTipText(currentFile.getAbsolutePath());
                addRecentFile(currentFile);
                saveRecentFiles();
            } catch (InterruptedException | ExecutionException | FileNotFoundException ex) {
                Exceptions.printStackTrace(ex);
            }
        }

    }
//
//    }
//    private QueryNode queryNode;

    public JPQLEditorTopComponent() {
        initComponents();
        setName(Bundle.CTL_JQPLEditorTopComponent());
        setToolTipText(Bundle.HINT_JQPLEditorTopComponent());
        connectionCombo.setRenderer(new ConnectionListRenderer());
        queryEditor.getActionMap().put("executeQuery", executeQueryAction);
        queryEditor.getInputMap().put(KeyStroke.getKeyStroke(KeyEvent.VK_ENTER, InputEvent.CTRL_DOWN_MASK), "executeQuery");
        queryEditor.getDocument().addDocumentListener(new DocumentListener() {

            @Override
            public void insertUpdate(DocumentEvent e) {
                modifiedContent();
            }

            @Override
            public void removeUpdate(DocumentEvent e) {
                modifiedContent();
            }

            @Override
            public void changedUpdate(DocumentEvent e) {
                modifiedContent();
            }

        });
        associateLookup(new AbstractLookup(ic));
        executeQueryAction.setEnabled(false);
        setDisplayName("Unsaved");
        setToolTipText("Content not saved");
        JFrame mainWindow = (JFrame) WindowManager.getDefault().getMainWindow();
//        mainWindow.setDefaultCloseOperation(JFrame.DO_NOTHING_ON_CLOSE);
//        setActivatedNodes(new Node[]{queryNode = new QueryNode()});
//     Lookup l = new ProxyLookup(Lookups.fixed( new Object[] {queryNode=new QueryNode(), new AbstractLookup(ic)}) );
//        associateLookup(l);
//        associateLookup(Lookups.fixed(queryNode=new QueryNode()));
    }

    private void addRecentFile(File file) {
        if (!recentFiles.contains(file)) {
            recentFiles.add(file);
            if (recentFiles.size() > 10) {
                recentFiles = new LinkedList<>(recentFiles.subList(recentFiles.size() - 10, recentFiles.size()));
            }
        }
    }

    public void loadContent(File file) throws IOException {
        currentFile = file;
        queryEditor.setText(new String(Files.readAllBytes(currentFile.toPath())));
        setDisplayName(currentFile.getName());
        setToolTipText(currentFile.getAbsolutePath());
        addRecentFile(currentFile);
        saveRecentFiles();
    }

    private void saveRecentFiles() throws FileNotFoundException {
        File file = new File(AppFiles.getAndCreateConfigDir(), "recentFiles.dat");
        try (PrintWriter writer = new PrintWriter(file)) {
            recentFiles.stream().forEach((f) -> {
                writer.println(f);
            });
        }
    }

    private void loadRecentFiles() throws IOException {
        File file = new File(AppFiles.getAndCreateConfigDir(), "recentFiles.dat");
        if (file.exists()) {
            Files.lines(file.toPath()).forEach((String line) -> {
                File f = new File(line);
                recentFiles.add(f);
//                ic.set(Collections.singletonList(new RecentFile(f)), null);
            });
        }
    }

    public static Set<File> readRecentFiles() throws IOException {
        File file = new File(AppFiles.getAndCreateConfigDir(), "recentFiles.dat");
        Set<File> set = new HashSet<>();
        if (file.exists()) {
            Files.lines(file.toPath()).forEach((String line) -> {
                File f = new File(line);
                set.add(f);
            });
        }
        return set;
    }

    public Set<File> getRecentFiles() {
        return new HashSet<>(recentFiles);
    }

    private void modifiedContent() {
//        if (getLookup().lookup(QuerySavable.class) == null) {
//            ic.add(new QuerySavable(queryEditor.getText()));
//        }
//        queryNode.fire(true);
//        ic.add(impl);
    }

    public void setUnitNames(List<String> unitNames) {
        DefaultComboBoxModel<String> model = new DefaultComboBoxModel<>();
        unitNames.stream().forEach((unitName) -> {
            model.addElement(unitName);
        });
        unitnamesCombo.setModel(model);
        if (model.getSize() > 0) {
            unitnamesCombo.setSelectedIndex(0);
        }
    }

    public void setConnections(List<Connection> connections) {
        this.connections = connections;
    }

    private void loadConnectionComboOptions(String unitname) {
        DefaultComboBoxModel<Connection> model = new DefaultComboBoxModel<>();
        try {
            Connection defaultConnection = PersistenceUnitHelper.getDefaultConnection(unitname);
            if (defaultConnection != null) {
                model.addElement(defaultConnection);
            }
        } catch (SAXException | IOException | XPathExpressionException | ParserConfigurationException ex) {
            Exceptions.printStackTrace(ex);
        }
        connections.stream().forEach((connection) -> {
            model.addElement(connection);
        });
        connectionCombo.setModel(model);
        if (model.getSize() > 0) {
            connectionCombo.setSelectedIndex(0);
        }
    }
    
    public void pasteQuery(String query) throws BadLocationException {
        queryEditor.getDocument().insertString(queryEditor.getCaretPosition(), query, null);
    }

    private void setResult(EntityManager em, List<Object> list) {
        DefaultTableModel model = new DefaultTableModel() {

            @Override
            public boolean isCellEditable(int row, int column) {
                return false;
            }

        };
        if (!list.isEmpty()) {
            Object rowToTest = list.get(0);
            if (rowToTest == null) {
                model = new DefaultTableModel(new String[]{"Value"}, 0);
                for (Object object : list) {
                    model.addRow(new Object[]{object});
                }
            } else if (ResultTypes.isSimpleType(rowToTest)) {
                model = new DefaultTableModel(new String[]{"Value"}, 0);
                for (Object object : list) {
                    model.addRow(new Object[]{object});
                }
            } else if (ResultTypes.isArray(rowToTest)) {
                Object[] array = (Object[]) rowToTest;
                String[] columnNames = new String[array.length];
                for (int i = 0; i < columnNames.length; i++) {
                    columnNames[i] = String.valueOf(i);
                }
                model = new DefaultTableModel(columnNames, 0);
                for (Object object : list) {
                    model.addRow((Object[]) object);
                }
            } else { //assume is a bean
                try {
                    PropertyDescriptor[] properties = Introspector.getBeanInfo(rowToTest.getClass()).getPropertyDescriptors();
                    String[] columnNames = new String[properties.length];
                    for (int i = 0; i < columnNames.length; i++) {
                        columnNames[i] = properties[i].getName();
                    }
                    model = new DefaultTableModel(columnNames, 0);
                    for (Object object : list) {
                        try {
                            Object[] row = new Object[properties.length];
                            for (int i = 0; i < properties.length; i++) {
//                                if(em.getEntityManagerFactory().getPersistenceUnitUtil().isLoaded(object, properties[i].getName())) {
                                row[i] = properties[i].getReadMethod().invoke(object);
//                                }else{
//                                    row[i]="Not loaded";
//                                }
                            }
                            model.addRow(row);
                        } catch (IllegalAccessException | InvocationTargetException ex) {

                        }
                    }
                } catch (IntrospectionException ex) {
                    Exceptions.printStackTrace(ex);
                }
            }
        }
        tableResult.setModel(model);
    }

    public void validateExecuteAction() {
        executeQueryAction.setEnabled(unitnamesCombo.getSelectedIndex() != -1 && connectionCombo.getSelectedIndex() != -1);
    }

    /**
     * This method is called from within the constructor to initialize the form.
     * WARNING: Do NOT modify this code. The content of this method is always
     * regenerated by the Form Editor.
     */
    // <editor-fold defaultstate="collapsed" desc="Generated Code">//GEN-BEGIN:initComponents
    private void initComponents() {

        fileChooser = new javax.swing.JFileChooser();
        jPanel1 = new javax.swing.JPanel();
        jButton1 = new javax.swing.JButton();
        unitnamesCombo = new javax.swing.JComboBox();
        jLabel1 = new javax.swing.JLabel();
        jLabel2 = new javax.swing.JLabel();
        connectionCombo = new javax.swing.JComboBox();
        busyIndicator = new org.jdesktop.swingx.JXBusyLabel();
        jSplitPane1 = new javax.swing.JSplitPane();
        jScrollPane2 = new javax.swing.JScrollPane();
        tableResult = new javax.swing.JTable();
        jScrollPane3 = new javax.swing.JScrollPane();
        queryEditor = new javax.swing.JEditorPane();

        jButton1.setAction(executeQueryAction);
        org.openide.awt.Mnemonics.setLocalizedText(jButton1, org.openide.util.NbBundle.getMessage(JPQLEditorTopComponent.class, "JPQLEditorTopComponent.jButton1.text_1")); // NOI18N

        unitnamesCombo.addActionListener(new java.awt.event.ActionListener() {
            public void actionPerformed(java.awt.event.ActionEvent evt) {
                unitnamesComboActionPerformed(evt);
            }
        });

        org.openide.awt.Mnemonics.setLocalizedText(jLabel1, org.openide.util.NbBundle.getMessage(JPQLEditorTopComponent.class, "JPQLEditorTopComponent.jLabel1.text")); // NOI18N

        org.openide.awt.Mnemonics.setLocalizedText(jLabel2, org.openide.util.NbBundle.getMessage(JPQLEditorTopComponent.class, "JPQLEditorTopComponent.jLabel2.text")); // NOI18N

        connectionCombo.addActionListener(new java.awt.event.ActionListener() {
            public void actionPerformed(java.awt.event.ActionEvent evt) {
                connectionComboActionPerformed(evt);
            }
        });

        org.openide.awt.Mnemonics.setLocalizedText(busyIndicator, org.openide.util.NbBundle.getMessage(JPQLEditorTopComponent.class, "JPQLEditorTopComponent.busyIndicator.text")); // NOI18N

        javax.swing.GroupLayout jPanel1Layout = new javax.swing.GroupLayout(jPanel1);
        jPanel1.setLayout(jPanel1Layout);
        jPanel1Layout.setHorizontalGroup(
            jPanel1Layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
            .addGroup(jPanel1Layout.createSequentialGroup()
                .addContainerGap(javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE)
                .addComponent(jLabel1)
                .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
                .addComponent(unitnamesCombo, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE)
                .addGap(18, 18, 18)
                .addComponent(jLabel2)
                .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
                .addComponent(connectionCombo, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE)
                .addGap(18, 18, 18)
                .addComponent(jButton1)
                .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
                .addComponent(busyIndicator, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE)
                .addContainerGap())
        );
        jPanel1Layout.setVerticalGroup(
            jPanel1Layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
            .addGroup(jPanel1Layout.createSequentialGroup()
                .addContainerGap()
                .addGroup(jPanel1Layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
                    .addGroup(jPanel1Layout.createParallelGroup(javax.swing.GroupLayout.Alignment.BASELINE)
                        .addComponent(jButton1)
                        .addComponent(connectionCombo, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE)
                        .addComponent(jLabel2)
                        .addComponent(unitnamesCombo, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE)
                        .addComponent(jLabel1))
                    .addComponent(busyIndicator, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE))
                .addGap(0, 0, Short.MAX_VALUE))
        );

        jSplitPane1.setDividerLocation(400);
        jSplitPane1.setOrientation(javax.swing.JSplitPane.VERTICAL_SPLIT);

        jScrollPane2.setViewportView(tableResult);

        jSplitPane1.setRightComponent(jScrollPane2);

        queryEditor.setContentType("text/java"); // NOI18N
        jScrollPane3.setViewportView(queryEditor);

        jSplitPane1.setLeftComponent(jScrollPane3);

        javax.swing.GroupLayout layout = new javax.swing.GroupLayout(this);
        this.setLayout(layout);
        layout.setHorizontalGroup(
            layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
            .addGroup(layout.createSequentialGroup()
                .addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
                    .addComponent(jPanel1, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE)
                    .addGroup(layout.createSequentialGroup()
                        .addContainerGap()
                        .addComponent(jSplitPane1, javax.swing.GroupLayout.DEFAULT_SIZE, 693, Short.MAX_VALUE)))
                .addContainerGap())
        );
        layout.setVerticalGroup(
            layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
            .addGroup(layout.createSequentialGroup()
                .addComponent(jPanel1, javax.swing.GroupLayout.PREFERRED_SIZE, 33, javax.swing.GroupLayout.PREFERRED_SIZE)
                .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
                .addComponent(jSplitPane1, javax.swing.GroupLayout.DEFAULT_SIZE, 351, Short.MAX_VALUE)
                .addContainerGap())
        );
    }// </editor-fold>//GEN-END:initComponents

    private void connectionComboActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_connectionComboActionPerformed
        validateExecuteAction();
    }//GEN-LAST:event_connectionComboActionPerformed

    private void unitnamesComboActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_unitnamesComboActionPerformed
        validateExecuteAction();
        loadConnectionComboOptions((String) unitnamesCombo.getSelectedItem());
    }//GEN-LAST:event_unitnamesComboActionPerformed

    // Variables declaration - do not modify//GEN-BEGIN:variables
    private org.jdesktop.swingx.JXBusyLabel busyIndicator;
    private javax.swing.JComboBox connectionCombo;
    private javax.swing.JFileChooser fileChooser;
    private javax.swing.JButton jButton1;
    private javax.swing.JLabel jLabel1;
    private javax.swing.JLabel jLabel2;
    private javax.swing.JPanel jPanel1;
    private javax.swing.JScrollPane jScrollPane2;
    private javax.swing.JScrollPane jScrollPane3;
    private javax.swing.JSplitPane jSplitPane1;
    private javax.swing.JEditorPane queryEditor;
    private javax.swing.JTable tableResult;
    private javax.swing.JComboBox unitnamesCombo;
    // End of variables declaration//GEN-END:variables
    @Override
    public void componentOpened() {
        try {
            loadRecentFiles();
        } catch (IOException ex) {
            Exceptions.printStackTrace(ex);
        }
    }

    @Override
    public void componentClosed() {
        // TODO add custom code on component closing
    }

    void writeProperties(java.util.Properties p) {
        // better to version settings since initial version as advocated at
        // http://wiki.apidesign.org/wiki/PropertyFiles
        p.setProperty("version", "1.0");
        // TODO store your settings
    }

    void readProperties(java.util.Properties p) {
        String version = p.getProperty("version");
        // TODO read your settings according to their version
    }
}
